package com.sm.healthy.ui.fragment

import android.bluetooth.BluetoothAdapter.ACTION_REQUEST_ENABLE
import android.bluetooth.BluetoothGatt
import android.content.Context
import android.content.Intent
import android.location.LocationManager
import android.os.Bundle
import android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS
import androidx.appcompat.app.AlertDialog
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import com.sm.healthy.utils.CacheUtil
import com.blackflagbin.kcommon.utils.PermissionUtils
import com.clj.fastble.callback.BleGattCallback
import com.clj.fastble.callback.BleNotifyCallback
import com.clj.fastble.callback.BleScanCallback
import com.clj.fastble.data.BleDevice
import com.clj.fastble.exception.BleException
import com.lishuaihua.toast.ToastUtils
import com.sm.healthy.R
import com.sm.healthy.base.BaseFragment
import com.sm.healthy.base.handle.TopHandle
import com.sm.healthy.data.bean.FetalEntity
import com.sm.healthy.data.bean.MoveBean
import com.sm.healthy.databinding.FragmentFetalHeartMBinding
import com.sm.healthy.model.FetalHeartModel
import com.sm.healthy.utils.BleUtils
import com.sm.healthy.utils.DataUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import me.hgj.jetpackmvvm.ext.nav
import me.hgj.jetpackmvvm.ext.navigateAction
import java.lang.ref.WeakReference
import java.text.SimpleDateFormat
import java.util.*
import kotlin.collections.ArrayList


/**
 * A simple [Fragment] subclass.
 * Use the [FetalHeartMFragment.newInstance] factory method to
 * create an instance of this fragment.
 */
class FetalHeartMFragment :
    BaseFragment<FetalHeartModel, FragmentFetalHeartMBinding>() {
    companion object {
        const val TAIXINYI_BLE_DEVICE_NAME = "sunmind-taixinyi"
        const val REQUEST_CODE_OPEN_GPS = 2
        const val REQUEST_ENABLE_BT = 1
    }

    private val bleUtils by lazy {
        BleUtils(TAIXINYI_BLE_DEVICE_NAME)
    }
    private var timeRecord = 0L

    override fun layoutId(): Int {
        return R.layout.fragment_fetal_heart_m
    }

    override fun initView(savedInstanceState: Bundle?) {
        mDatabind.model = mViewModel
        mDatabind.handle = TopHandle(nav())

        mDatabind.tvHistory.setOnClickListener {
            nav().navigateAction(R.id.historyDataFragment)
        }

        mDatabind.tvBtnStart.setOnClickListener {
            startMeasure()
        }
        lifecycleScope.launch(Dispatchers.Main) {
            dataFlow.collect {
                getFHR(it)
            }
        }
    }

    override fun onStop() {
        super.onStop()
        bleUtils.cancelScan()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        bleUtils.disconnect()
    }

    private fun startMeasure() {
        if (!bleUtils.checkBle()) return
        if (!mViewModel.isStart.get())
            PermissionUtils.location(requireActivity(), {
                startScan()
            })
        else {
            mViewModel.isStart.set(false)
            mDatabind.tvBtnStart.text = "开始测量"
            mDatabind.tvBtnStart.setBackgroundResource(R.drawable.bg_btn_blue_shape)
            bleUtils.disconnect()
            saveData()
        }
    }

    private fun saveData() {
        if (recoderData2.size < 3) mViewModel.toast.postValue("数据过少，保存失败！")
        else dealData()
        clearDataRestart()
    }

    private fun dealData() {
        //去掉一个最大值和最小值
        recoderData2.remove(Collections.max(recoderData2))
        recoderData2.remove(Collections.min(recoderData2))

        val heartRateAve: Int = DataUtils.getAve(recoderData2)
        mViewModel.insert(
            FetalEntity(
                CacheUtil.getCurUid(),
                avgFetalRate = heartRateAve,
                allTimes = mViewModel.fetalMovementCount,
                timeRecodeLong = System.currentTimeMillis() - timeRecord
            )
        )
    }

    private fun startScan() {
        when {
            bleUtils.bleIsOpen().not() -> openBle()
            checkGPSIsOpen().not() -> openGPS()
            else -> scan()
        }
    }

    private fun finishScan(result: BleDevice) {
        bleUtils.connect(result, MyBleGattCallback(this))
    }

    private fun scan() {
        bleUtils.startScan(MyBleScanCallback(this))
    }

    private fun connectSuccess() {
        timeRecord = System.currentTimeMillis()
        mViewModel.isStart.set(true)
        mDatabind.tvBtnStart.text = "停止测量"
        mDatabind.tvBtnStart.setBackgroundResource(R.drawable.bg_btn_red_shape)
        bleUtils.notifyCharacteristic(object : BleNotifyCallback() {
            override fun onNotifySuccess() {
            }

            override fun onNotifyFailure(exception: BleException?) {
            }

            override fun onCharacteristicChanged(data: ByteArray?) {
                delData(data ?: return)
            }

        })

    }

    var moveX = 0
    private var moveLineX = 0
    private val format = SimpleDateFormat("HH:mm:ss")

    var recoderData: ArrayList<MoveBean> = ArrayList()
    var recoderData2: ArrayList<Int> = ArrayList()

    private val movetTime by lazy(LazyThreadSafetyMode.NONE) {
        mDatabind.tableView.tableWidth / (17.0f * 60.0f) / 4.0f// 1min\1cm
    }

    private val _stateFlow = MutableStateFlow(ByteArray(0))
    private val dataFlow = _stateFlow.sample(250).filter { it.isNotEmpty() }.catch { }


    val test: ByteArray = byteArrayOf(85, -86, 3, 107, 0, 9, 0, 3, 80, -57)

    fun delData(data: ByteArray) {
        val type = data[2].toInt()

        when (type) {
            0x03 -> _stateFlow.value = data
        }
    }

    private fun getFHR(data: ByteArray) {
        val fhr1 = data[3].toInt().and(0xff)
        val fhr2 = data[4]
        val toco = data[5]
        val amf = data[6]

        //是否胎动
        val isFetalMovement = data[7].toInt().shr(2).and(0x01) > 0

        if (isFetalMovement) {
            mViewModel.fetalMovementCount++
            mViewModel.fetalMovementStr.set(mViewModel.fetalMovementCount.toString())
        }
        if (fhr1 > 0)
            recoderData2.add(fhr1)

        moveLineX++

        mViewModel.fhrStr.set(fhr1.toString())
        val px: Float = (mDatabind.tableView.tableHeight
                - (240.0f - fhr1) * mDatabind.tableView.tableHeight / 210.0f)
        val moveBean = MoveBean()
        moveBean.moveY = mDatabind.tableView.tableHeight - px

        moveBean.moveX = mDatabind.tableView.getMoveLineToatle(moveLineX) - 10

        if (mDatabind.tableView.tableWidth <= mDatabind.tableView.moveToatle) {
            if (recoderData.size > 15000) {
                clearDataRestart()
            }
            mDatabind.tableView.setTimeMoveEnd(true)
        } else {
            mDatabind.tableView.setTimeMoveEnd(false)
            moveX++
        }

        val linearParams: ConstraintLayout.LayoutParams =
            mDatabind.lineView.layoutParams as ConstraintLayout.LayoutParams
        linearParams.width = moveBean.moveX.toInt()
        mDatabind.lineView.layoutParams = linearParams


        mDatabind.lineView.setMoveWidth(mDatabind.tableView.tableWidth);
        val date: String = format.format(System.currentTimeMillis())
        moveBean.timeConfig = date
        recoderData.add(moveBean)

        mDatabind.lineView.setMoveData(recoderData)
        mDatabind.lineView.setMoveTime(movetTime)
        mDatabind.lineView.setCanDrawHeight(mDatabind.tableView.tableHeight.toFloat())
        mDatabind.lineView.setDrawHeightError(mDatabind.tableView.tableHeightError.toFloat())
        mDatabind.tableView.setMoveX(moveX, movetTime, date)

    }

    private fun clearDataRestart() {
        recoderData.clear()
        moveLineX = 0
        moveX = 0
        recoderData2.clear()
    }

    private fun checkGPSIsOpen(): Boolean {
        val locationManager =
            activity?.getSystemService(Context.LOCATION_SERVICE) as LocationManager
        return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
    }

    private fun openBle() {
        val enableBtIntent = Intent(ACTION_REQUEST_ENABLE)
        startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT)
    }

    private fun openGPS() {
        AlertDialog.Builder(requireActivity()).setMessage("开启GPS定位").setNegativeButton("取消", null)
            .setPositiveButton("打开") { _, _ ->
                val intent = Intent(ACTION_LOCATION_SOURCE_SETTINGS)
                startActivityForResult(intent, REQUEST_CODE_OPEN_GPS)
            }.show()
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        when (requestCode) {
            REQUEST_ENABLE_BT -> if (checkGPSIsOpen()) {
                scan()
            } else {
                openGPS()
            }
            REQUEST_CODE_OPEN_GPS -> if (bleUtils.bleIsOpen()) {
                scan()
            } else {
                openBle()
            }
        }
    }

    class MyBleScanCallback(fragment: FetalHeartMFragment) : BleScanCallback() {
        private val myFragment: WeakReference<FetalHeartMFragment> = WeakReference(fragment)

        override fun onScanStarted(success: Boolean) {
            myFragment.get()?.showLoading("蓝牙扫描中")
        }

        override fun onScanning(result: BleDevice) {
            myFragment.get()?.finishScan(result)
        }

        override fun onScanFinished(scanResultList: List<BleDevice>) {
            myFragment.get()?.dismissLoading()
            if (scanResultList.isEmpty()) {
                ToastUtils.show("扫描失败")
            }
        }
    }

    class MyBleGattCallback(fragment: FetalHeartMFragment) : BleGattCallback() {
        private val myFragment: WeakReference<FetalHeartMFragment> = WeakReference(fragment)

        override fun onStartConnect() {
            myFragment.get()?.showLoading("蓝牙连接中")
        }

        override fun onConnectFail(bleDevice: BleDevice?, exception: BleException?) {
            myFragment.get()?.dismissLoading()
        }

        override fun onConnectSuccess(
            bleDevice: BleDevice?,
            gatt: BluetoothGatt?,
            status: Int
        ) {
            myFragment.get()?.dismissLoading()
            val services = gatt?.services
            myFragment.get()?.bleUtils?.getService(services, "fff0")
            myFragment.get()?.connectSuccess()
            /*myFragment.get()
                ?.writeDataToBle(if (Config.isSingleEcg) GET_ECG2_WAVEFORM_ON else GET_ECG12_WAVEFORM_ON)*/
        }

        override fun onDisConnected(
            isActiveDisConnected: Boolean,
            device: BleDevice?,
            gatt: BluetoothGatt?,
            status: Int
        ) {
            myFragment.get()?.dismissLoading()
        }

    }
}